#include "Mesh.h"
#include <fstream>
#include <Windows.h>


using std::ifstream;



Mesh::Mesh() :	m_iSubsets(1), _material(), bShadowCaster(true), bShadowReceiver(true), bUsePCSS(false), bLit(true), g_context(0), /*m_World(XMFLOAT4X4()),*/ 
				 g_device(0), vertex_buffer(0), index_buffer(0), 
				 input_layout(0), num_vertices(0), num_indices(0),
				 scale(1.0f)
{
	XMStoreFloat4x4(&m_World, XMMatrixIdentity());
}
	
Mesh::~Mesh()
{
	try
	{
		if (g_context)
			g_context->Release();

		if (g_device)
			g_device->Release();

		if (vertex_buffer)
			vertex_buffer->Release();

		if (index_buffer)
			index_buffer->Release();

		if (input_layout)
			input_layout->Release();

		if (_material.pTextureRV10 != 0) {
			_material.pTextureRV10->Release();
			_material.pTextureRV10 = 0;
		}
	
		if (_material.pTextureNormalPRV10 != 0) {
			_material.pTextureNormalPRV10->Release();
			_material.pTextureNormalPRV10 = 0;
		}
		
		if (_material.pTextureSpecularPRV10 != 0) {
			_material.pTextureSpecularPRV10->Release();
			_material.pTextureSpecularPRV10 = 0;
		}

	}
	catch(...)
	{
		MessageBoxA(0, "exception thrown in  destructor", 0, 0);
	}
}

XMMATRIX Mesh::getWorldMatrix(int subset) const
{
	return XMLoadFloat4x4(&m_World);
}


void Mesh::load(const char* filename)
{
	obj_loader.load(g_device, filename, &vertex_buffer, &index_buffer, _material, num_vertices, num_indices);

}


void Mesh::setMaterial(ID3DX11Effect* fx) const
{
	

	ID3DX11EffectShaderResourceVariable* fx_tex_diffuse  = fx->GetVariableByName("tex_diffuse")->AsShaderResource();
	ID3DX11EffectShaderResourceVariable* fx_tex_specular = fx->GetVariableByName("tex_specular")->AsShaderResource();
	ID3DX11EffectShaderResourceVariable* fx_tex_normal   = fx->GetVariableByName("tex_normal")->AsShaderResource();
	ID3DX11EffectVariable*	fx_mat_params 				 = fx->GetVariableByName("mat_params");



	fx_mat_params->SetRawValue((void*)&_material, 0, 64);

	if (_material.pTextureRV10 != (ID3D11ShaderResourceView*)0)
		fx_tex_diffuse->SetResource(_material.pTextureRV10);

	if (_material.pTextureSpecularPRV10 != (ID3D11ShaderResourceView*)0)
		fx_tex_specular->SetResource(_material.pTextureSpecularPRV10);

	if (_material.pTextureNormalPRV10 != (ID3D11ShaderResourceView*)0)
		fx_tex_normal->SetResource(_material.pTextureNormalPRV10);



}

void Mesh::rotateLocalAxis(int axis, float amount)
{

	XMMATRIX m_rot = XMMatrixIdentity();
	int r1 = 0; int r2 = 1;

	if (axis == 0)	// X
	{
		m_rot = XMMatrixRotationX(amount);
		r1 = 1;
		r2 = 2;
		
	}
	else if (axis == 1)	// Y
	{
		m_rot = XMMatrixRotationY(amount);
		r1 = 0;
		r2 = 2;
	}
	else if (axis == 2) // Z
	{
		m_rot = XMMatrixRotationZ(amount);
		r1 = 0;
		r2 = 1;
	}

	XMFLOAT4 v1 = XMFLOAT4(m_World(r1, 0), m_World(r1, 1), m_World(r1, 2), m_World(r1, 3));
	XMFLOAT4 v2 = XMFLOAT4(m_World(r2, 0), m_World(r2, 1), m_World(r2, 2), m_World(r2, 3));

	// Transform axes
	XMVECTOR new_v1 = XMVector4Transform(XMLoadFloat4(&v1), m_rot);
	XMVECTOR new_v2 = XMVector4Transform(XMLoadFloat4(&v2), m_rot);

	XMStoreFloat4(&v1, new_v1);
	XMStoreFloat4(&v2, new_v2);
	
	// 
	m_World(r1,0) = v1.x; m_World(r1,1) = v1.y; m_World(r1,2) = v1.z; m_World(r1,3) = v1.w;
	m_World(r2,0) = v2.x; m_World(r2,1) = v2.y; m_World(r2,2) = v2.z; m_World(r2,3) = v2.w;

}

void Mesh::setPos(float x, float y, float z)
{
	m_World._41 = x;
	m_World._42 = y;
	m_World._43 = z;
}

void Mesh::translate(float x, float y, float z)
{
	m_World._41 += x;
	m_World._42 += y;
	m_World._43 += z;

}


void Mesh::bindVertexIndexBuffers()
{
	UINT stride = sizeof(Vertex); UINT offset = 0;
	g_context->IASetVertexBuffers(0, 1, &vertex_buffer, &stride, &offset);
	g_context->IASetIndexBuffer(index_buffer, DXGI_FORMAT_R32_UINT, 0);
}

void Mesh::init(const char* filename, ID3D11Device* g_dev, ID3D11DeviceContext* g_ctx)
{
	g_context = g_ctx;
	g_context->AddRef();

	g_device = g_dev;
	g_device->AddRef();

	if (filename)
		load(filename);

}

void Mesh::draw(int subset, ID3DX11Effect* fx, const char* technique) const
{


	UINT stride = sizeof(Vertex); UINT offset = 0;
	
	g_context->IASetVertexBuffers(0, 1, &vertex_buffer, &stride, &offset);
	g_context->IASetIndexBuffer(index_buffer, DXGI_FORMAT_R32_UINT, 0);

	ID3DX11EffectTechnique* tech = fx->GetTechniqueByName(technique);
	tech->GetPassByIndex(0)->Apply(0, g_context);

	g_context->DrawIndexed(num_indices, 0, 0);

}



void Mesh::setDiffuse(float x, float y, float z)
{
	_material.vDiffuse.x = x;
	_material.vDiffuse.y = y;
	_material.vDiffuse.z = z;
}

void Mesh::setSpecular(float x, float y, float z)
{
	_material.vSpecular.x = x;
	_material.vSpecular.y = y;
	_material.vSpecular.z = z;
}

void Mesh::createDiffuseTexture(const wchar_t* str)
{
	HRESULT hres = D3DX11CreateShaderResourceViewFromFile(g_device, str, 0, 0, &(_material.pTextureRV10), 0);
	if (FAILED(hres))
		MessageBoxA(0, "Error creating diffuse texture", 0, 0);
	else
		_material.vIsTextured.x = 1.0f;

}


void Mesh::setExponent(float v)
{
	_material.vDiffuse.w = v;
}

void Mesh::setCutOffAngle(float v)
{
	_material.vSpecular.w = v;
}


void Mesh::setScale(float v) 
{ 
	
	
	scale = max(v, 1.0f); 

	m_World._11 = m_World._33 = scale;
}